package com.chunlin.generic;

import java.util.ArrayList;
import java.util.List;

public class GenericTest{
    //包装类
    //在Java中，由于基本类型不是继承自Object，为了在泛型代码中可以支持基本类型，Java给每个基本类型都对应了一个包装类型
    //除了 int 和 char， 其余基本类型的包装类都是首字母大写
    //int -> Integer    char -> Character
    public static void test1(){
        int i = 10;

        //装箱是指将基本数据类型转换为对应的包装类对象的过程
        Integer boxA = Integer.valueOf(i);
        Integer boxB = Integer.valueOf(i);

        //拆箱是指将包装类对象转换为对应的基本数据类型的过程
        int j = boxA.intValue();
        System.out.println(i);
        System.out.println(boxA);
        System.out.println(boxB);
        System.out.println(j);

        //Java 编译器会自动进行这种转换，这个过程被称为自动装箱（Autoboxing）
        int k = 20;
        Integer boxC = k;
        Integer boxD = (Integer) k;

        //Java 编译器也会自动进行这种转换，这个过程被称为自动拆箱（Auto-unboxing）
        int l = boxC;
        int m = (int)boxD;
        System.out.println(k);
        System.out.println(boxC);
        System.out.println(boxD);
        System.out.println(l);
        System.out.println(m);

    }

    //
    public static void test2(){
        //Java的Integer类缓存了从-128到127之间的整数对象
        //这意味着，对于这个范围内的整数，Java不会每次都创建新的Integer对象，而是使用缓存中的实例
        //对于超过这个范围的整数，Java会创建新的Integer对象
        Integer a = 127;
        Integer b = 127;
        Integer c = 128;
        Integer d = 128;
        System.out.println(a == b);  //true
        System.out.println(c == d);  //false
    }
    public static void test3(){
        MyAarray<Integer> myAarray = new MyAarray<>();
        myAarray.setVal(0, 10);
        myAarray.setVal(1, 12);
        int retPos = myAarray.getPos(1); //自动拆箱
        System.out.println(retPos);
    }
    //注意：泛型只能接受类，所有的基本数据类型必须使用包装类！
    public static void test4(){
        //当编译器可以根据上下文推导出类型实参时，可以省略类型实参的填写
        MyAarray<Integer> myAarray1 = new MyAarray<Integer>();
        myAarray1.setVal(0, 1);
        MyAarray<Integer> myAarray2 = new MyAarray<>();
        int retPos = myAarray1.getPos(0); //自动拆箱
        System.out.println(retPos);

        //父类数组,可以保存子类的数据
        Number[] numbers = new Number[10];
        numbers[0] = new Integer(1);
        numbers[1] = new Double(1.0);

//        Integer[] array1 = myAarray1.getArray();
        MyAarray<Integer> myAarray3 = new MyAarray<>(Integer.class, 10);
        myAarray3.setVal(0, 1);
        Integer[] array2 = myAarray3.getArray();

    }
    public static void test5(){
        MyArray2<Integer> myArray2 = new MyArray2<>();       // 正常，因为 Integer 是 Number 的子类型
//        MyArray2<String> myArray21 = new MyArray2<String>(); // 编译错误，因为 String 不是 Number 的子类型
    }

    //这个代码会带来新的问题，如果现在泛型的类型设置的不是String，而是Integer
//    public static void fun(Message<String> temp){
//        System.out.println(temp.getMessage());
//    }

    //可以接收所有的泛型类型，但是又不能够让用户随意修改。这种情况就需要使用通配符"?"来处理
    public static void fun(Message<?> temp) {
        System.out.println(temp.getMessage());
    }

    public static void test6(){
        Message<String> message = new Message<>();
        message.setMessage("hello");
        Message<Integer> message1 = new Message<>();
        message1.setMessage(123);
        fun(message);
        fun(message1); // 出现错误，只能接收String,使用通配符之后就可以正常的进行接收了
    }

    //使用上界通配符(不能进行写入数据，只能进行读取数据) <? extends Animal>
    //场景： 一个只读方法，用于打印动物的食物
    //在这个方法中，我们可以传递 List<Animal>, List<Dog>, 或者 List<Cat> 类型的列表
    // 因为我们只需要调用它们共有的方法 feed()，这个方法在所有 Animal 类型和它的子类型中都是有效的
    //为什么不能写入： 这个方法接受任何类型的 Animal 的子类的列表，比如 List<Dog> 或 List<Cat>
    // 如果允许向这个方法中的列表添加一个 Dog 对象，当传入的是 List<Cat> 时，就会破坏集合的类型安全
    // 因此，只能进行读取操作，而不能写入任何特定类型的对象
    public static void printFeeding(List<? extends Animal> animals) {
        for (Animal animal : animals) {
            animal.feed();
        }
    }

    //使用下界通配符 <? super Dog>
    //场景： 添加 Dog 到列表的方法
    //在这个方法中，我们可以传递 List<Dog>, List<Animal>, 或者 List<Object> 类型的列表
    // 由于我们只向列表中添加 Dog 类型的对象，这是安全的，因为所有这些列表都可以接受 Dog 对象
    //为什么可以写入但不推荐读取： 这个方法接受 List<Dog>，List<Animal> 或 List<Object> 等可以包含 Dog 的任何父类的集合
    // 添加 Dog 到这样的列表是安全的，因为 Dog 是所有这些潜在列表类型的有效成员
    // 然而，从这样的列表中读取元素时，我们只能安全地假设返回的是 Object 类型，因为列表可以是 List<Object>
    // 但是我们可以通过instanceof来实际的检查对应的类型
    //只是代码中需要额外的类型检查和转换，这可能会使得代码稍显繁琐且运行效率略低
    public static void addDogToList(List<? super Dog> animals) {
        animals.add(new Dog()); // 安全地添加一个Dog或其子类

        for (Object obj : animals) {
            if (obj instanceof Animal) {
                ((Animal)obj).feed();
            }

            if (obj instanceof Dog) {
                ((Dog) obj).bark();
            }
        }
    }

    public static void test7(){
        List<Animal> animals = new ArrayList<>();
        animals.add(new Dog());
        animals.add(new Cat());

        printFeeding(animals);  // 正确使用，只进行读取操作

        List<Object> objects = new ArrayList<>();
        addDogToList(objects); // 正确使用，写入操作,以及有检查的读取操作
    }


    public static void test8(){}
    public static void test9(){}
    public static void test10(){}
    public static void test11(){}
    public static void test12(){}
    public static void test13(){}
    public static void test14(){}
    public static void test15(){}
    public static void test16(){}

    public static void main(String[] args) {
//        test1();
//        test2();
//        test3();
//        test4();
//        test5();
//        test6();
        test7();
//        test8();
//        test9();
//        test10();
//        test11();
//        test12();
//        test13();
//        test14();
//        test15();
//        test16();
//        test17();
//        test18();
//        test19();
    }
}
